// import qs from 'qs'
import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { GET_TOKEN, GET_TOKEN_KEY } from '../func/storage'
import { ElMessage } from 'element-plus'
import { AutoAxiosRequestConfig, Result, ErrorCodes, AutoConfig } from './http.d'
import * as Qs from 'qs'

// 1、根据当前请求信息生成请求key
function generateReqKey(config) {
  const { method, url, params, data } = config
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join('&')
}
// 2、将当前请求信息添加到pendingRequest对象中
const pendingRequest = new Map()
function addPendingRequest(config) {
  const requestKey = generateReqKey(config)
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel) => {
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, cancel)
      }
    })
}
// 3、检查是否存在重复请求，若存在则需要取消已发出的请求
function removePendingRequest(config) {
  const requestKey = generateReqKey(config)
  if (pendingRequest.has(requestKey)) {
    const cancelToken = pendingRequest.get(requestKey)
    cancelToken(requestKey)
    pendingRequest.delete(requestKey)
  }
}

// 导出Request类，可以用来自定义传递配置来创建实例
export class http {
  // axios 实例
  instance: AxiosInstance
  // 基础配置，url和超时时间
  baseConfig: AutoAxiosRequestConfig = {
    baseURL: '/',
    timeout: 60000,
    withCredentials: false,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    // transformRequest: [
    //   function transformRequest(data, headers) {
    //     return qs.stringify(data)
    //   }
    // ],
    filterData: true, // 自定义是否只返回 data 数据结构
  }

  constructor(config: AutoAxiosRequestConfig) {
    // 使用axios.create创建axios实例
    this.instance = axios.create(Object.assign(this.baseConfig, config))

    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        const TOKEN = GET_TOKEN()

        config.headers[GET_TOKEN_KEY()] = TOKEN || ''

        // 取消重复请求
        if (config?.clearRequest) {
          removePendingRequest(config)
          addPendingRequest(config)
        }
        return config
      },
      (error: any) => {
        // 请求错误，这里可以用全局提示框进行提示
        return Promise.reject(error)
      }
    )

    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const { config: AutoConfig } = response
        // 使用系统实例化时自定义返回数据格式方法
        if (config.autoResponse) return config.autoResponse(response)

        // 默认返回过滤数据只返回 data 数据结构
        return config.filterData ? Promise.resolve(response.data) : Promise.resolve(response)
      },
      (error: AxiosError) => {
        if (error.code === 'ERR_CANCELED') return // 取消重复请求的不用报错
        // 这里用来处理http常见错误，进行全局提示
        const errorMsg: ErrorCodes = {
          400: '请求错误',
          401: '未授权，请重新登录',
          403: '拒绝访问',
          404: '请求资源未找到',
          408: '请求超时',
          500: '服务器错误',
          501: '服务未实现',
          502: '网络错误',
          503: '服务不可用',
          504: '网络超时',
          505: 'HTTP版本不受支持',
        }
        const Status = error?.response?.status || 0

        const message = errorMsg[Status] || `${error.message}`
        // 这里错误消息可以使用全局弹框展示出来
        // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
        ElMessage({
          type: 'error',
          message: `${message}`,
          duration: 5000,
        })
        return Promise.reject(error.response)
      }
    )
  }

  public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<Result<T>>> {
    return this.instance.get(url, config)
  }

  public post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<Result<T>>> {
    return this.instance.post(url, data, config)
  }

  public put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<Result<T>>> {
    return this.instance.put(url, data, config)
  }

  public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<Result<T>>> {
    return this.instance.delete(url, config)
  }

  public requestJSON<T = any>(method: string = 'post', url: string, data: any, config = {}): Promise<AxiosResponse<Result<T>>> {
    method = method.toLowerCase()
    const jsonConfig = {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Content-Type': 'application/json',
      },
      transformRequest: [
        function transformRequest(data: any) {
          return JSON.stringify(data)
        },
      ],
      ...config,
    }

    switch (method) {
      case 'post':
        return this.post(url, data, jsonConfig)
      case 'get':
        return this.get(url, { params: data })
      case 'delete':
        return this.delete(url, { params: data })
      case 'put':
        return this.put(url, data)
      default:
        console.log(`不明的 method: ${method}`)
    }
  }
}

// 默认导出http实例
export default http
